﻿#region File and License Information
/*
<File>
	<License>
		Copyright © 2009 - 2017, Daniel Vaughan. All rights reserved.
		This file is part of Calcium (http://calciumsdk.net), 
		which is released under the MIT License.
		See file /Documentation/License.txt for details.
	</License>
	<CreationDate>2010-11-20 13:29:36Z</CreationDate>
</File>
*/
#endregion

using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;

using Outcoder.UI.Xaml.Controls.Design;

using Microsoft.Phone.Shell;

namespace Outcoder.UI.Xaml.Controls
{
    public class AppBarToggleButton : FrameworkElement,
        IApplicationBarIconButtonProvider
    {
        public ApplicationBarIconButton Button { get; set; }

        /* This handler is a field because there is an issue 
         * with the handler being cleaned up, and GC.KeepAlive doesn't work. */
        readonly EventHandler command1CanExecuteChangedHandler;
        readonly EventHandler command2CanExecuteChangedHandler;

        public AppBarToggleButton()
        {
            Button = new ApplicationBarIconButton
                     	{
                     		Text = "Text",
							/* IconUri must be set. */
							IconUri = new Uri("/Images/Unknown.png", UriKind.Relative) 
						};
            
            command1CanExecuteChangedHandler = Command1_CanExecuteChanged;
            command2CanExecuteChangedHandler = Command2_CanExecuteChanged;
        	
			//Loaded += delegate { UpdateToggleState(); };
	        Loaded += HandleLoaded;
	        Unloaded += HandleUnloaded;
        }

	    void HandleLoaded(object sender, RoutedEventArgs e)
	    {
			Button.Click -= ApplicationBarIconButtonClick;
			Button.Click += ApplicationBarIconButtonClick;
	    }

	    void HandleUnloaded(object sender, RoutedEventArgs e)
	    {
			Button.Click -= ApplicationBarIconButtonClick;
	    }

	    static void UpdateToggleState(DependencyObject d)
        {
            var button = d as AppBarToggleButton;
            if (button == null)
            {
                return;
            }
            button.UpdateToggleState();
        }

        void UpdateToggleState()
        {
            bool toggled = Toggled;

            UpdateText(toggled);
            UpdateIconUri(toggled);
            UpdateCommand(toggled);
        }

        static void UpdateText(DependencyObject d)
        {
            var button = d as AppBarToggleButton;
            if (button == null)
            {
                return;
            }
            button.UpdateText(button.Toggled);
        }

        void UpdateText(bool toggled)
        {
            Button.Text = toggled ? Text2 ?? Text1 : Text1;
        }

        static void UpdateIconUri(DependencyObject d)
        {
            var button = d as AppBarToggleButton;
            if (button == null)
            {
                return;
            }
            button.UpdateIconUri(button.Toggled);
        }

        void UpdateIconUri(bool toggled)
        {
            Button.IconUri = toggled
                ? IconUri2 ?? IconUri1 : IconUri1;
        }

        static void UpdateCommand(DependencyObject d)
        {
            var button = d as AppBarToggleButton;
            if (button == null)
            {
                return;
            }
            button.UpdateCommand(button.Toggled);
        }

        void UpdateCommand(bool toggled)
        {
			ICommand command2 = Command2;

			if (HasIsEnabledBinding())
			{
				return;
			}

			if (toggled && command2 != null)
            {
				IsEnabled = command2.CanExecute(CommandParameter2);
            }
            else
            {
                ICommand command = Command1;
				if (command != null)
				{
					IsEnabled = command.CanExecute(CommandParameter1);
				}
            }
        }

        #region IconUri1 Dependency Property

        public static DependencyProperty Icon1UriProperty = DependencyProperty.Register(
            "IconUri1",
            typeof(Uri),
            typeof(AppBarToggleButton),
            new PropertyMetadata(HandleIcon1Changed));

        static void HandleIcon1Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UpdateToggleState(d);
        }

        public Uri IconUri1
        {
            get
            {
                return (Uri)GetValue(Icon1UriProperty);
            }
            set
            {
                SetValue(Icon1UriProperty, value);
            }
        }

        #endregion

        #region IconUri2 Dependency Property

        public static DependencyProperty Icon2UriProperty
            = DependencyProperty.Register(
                "IconUri2",
                typeof(Uri),
                typeof(AppBarToggleButton),
                new PropertyMetadata(HandleIcon2Changed));

        static void HandleIcon2Changed(
            DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UpdateIconUri(d);
        }

        [Description("The location of the toggled state icon.")]
        public Uri IconUri2
        {
            get
            {
                return (Uri)GetValue(Icon2UriProperty);
            }
            set
            {
                SetValue(Icon2UriProperty, value);
            }
        }

        #endregion

        #region Command 1

        public static readonly DependencyProperty Command1Property
            = DependencyProperty.RegisterAttached(
                "Command1",
                typeof(ICommand),
                typeof(AppBarToggleButton),
                new PropertyMetadata(HandleCommand1Changed));

        public ICommand Command1
        {
            get
            {
                return (ICommand)GetValue(Command1Property);
            }
            set
            {
                SetValue(Command1Property, value);
            }
        }

        static void HandleCommand1Changed(DependencyObject d,
            DependencyPropertyChangedEventArgs e)
        {
            var button = d as AppBarToggleButton;
            if (button == null)
            {
                return;
            }

            if (e.OldValue != null)
            {
                ((ICommand)e.OldValue).CanExecuteChanged
                    -= button.command1CanExecuteChangedHandler;
            }

            if (e.NewValue != null)
            {
	            var newCommand = (ICommand)e.NewValue;
				newCommand.CanExecuteChanged 
					-= button.command1CanExecuteChangedHandler;
				newCommand.CanExecuteChanged
					+= button.command1CanExecuteChangedHandler;
            }
        }

        void Command1_CanExecuteChanged(object sender, EventArgs e)
        {
			if (Toggled && Command2 != null || HasIsEnabledBinding())
            {
                return;
            }

            ICommand command = Command1;
            if (command != null)
            {
                IsEnabled = command.CanExecute(CommandParameter1);
            }
        }

        public static readonly DependencyProperty CommandParameter1Property
            = DependencyProperty.RegisterAttached(
                "CommandParameter1",
                typeof(object),
                typeof(AppBarToggleButton),
                new PropertyMetadata(HandleCommandParameter1Changed));

        static void HandleCommandParameter1Changed(
            DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UpdateCommand(d);
        }

        public object CommandParameter1
        {
            get
            {
                return GetValue(CommandParameter1Property);
            }
            set
            {
                SetValue(CommandParameter1Property, value);
            }
        }
        #endregion

        #region Command 2

        public static readonly DependencyProperty Command2Property
            = DependencyProperty.RegisterAttached(
                "Command2",
                typeof(ICommand),
                typeof(AppBarToggleButton),
                new PropertyMetadata(HandleCommand2Changed));

        public ICommand Command2
        {
            get
            {
                return (ICommand)GetValue(Command2Property);
            }
            set
            {
                SetValue(Command2Property, value);
            }
        }

        static void HandleCommand2Changed(DependencyObject d,
            DependencyPropertyChangedEventArgs e)
        {
            var button = d as AppBarToggleButton;
            if (button == null)
            {
                return;
            }

            if (e.OldValue != null)
            {
                ((ICommand)e.OldValue).CanExecuteChanged
                    -= button.command2CanExecuteChangedHandler;
            }

            if (e.NewValue != null)
            {
                ((ICommand)e.NewValue).CanExecuteChanged
                    += button.command2CanExecuteChangedHandler;
            }
        }

        void Command2_CanExecuteChanged(object sender, EventArgs e)
        {
			if (!Toggled || HasIsEnabledBinding())
            {
                return;
            }

            ICommand command = Command2;
            if (command != null)
            {
                IsEnabled = command.CanExecute(CommandParameter2);
            }
        }

        public static readonly DependencyProperty CommandParameter2Property
            = DependencyProperty.RegisterAttached(
                "CommandParameter2",
                typeof(object),
                typeof(AppBarToggleButton),
                new PropertyMetadata(HandleCommandParameter2Changed));

        static void HandleCommandParameter2Changed(
            DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UpdateCommand(d);
        }

        public object CommandParameter2
        {
            get
            {
                return GetValue(CommandParameter2Property);
            }
            set
            {
                SetValue(CommandParameter2Property, value);
            }
        }
        #endregion

        #region IsEnabled

        public static readonly DependencyProperty IsEnabledProperty
            = DependencyProperty.RegisterAttached(
                "IsEnabled",
                typeof(bool),
                typeof(AppBarToggleButton),
                new PropertyMetadata(true, HandleIsEnabledChanged));

        public bool IsEnabled
        {
            get
            {
                return (bool)GetValue(IsEnabledProperty);
            }
            set
            {
                SetValue(IsEnabledProperty, value);
            }
        }

        static void HandleIsEnabledChanged(
            DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            AppBarToggleButton iconButton;
            if (e.NewValue != e.OldValue
                && (iconButton = d as AppBarToggleButton) != null
                && iconButton.Button != null)
            {
                iconButton.Button.IsEnabled = (bool)e.NewValue;
            }
        }

		bool HasIsEnabledBinding()
		{
			BindingExpression expression = GetBindingExpression(IsEnabledProperty);
			return expression != null;
		}

        #endregion

        #region Text 1

        public static readonly DependencyProperty Text1Property
            = DependencyProperty.RegisterAttached(
                "Text1",
                typeof(string),
                typeof(AppBarToggleButton),
                new PropertyMetadata(HandleText1Changed));

        public string Text1
        {
            get
            {
                return (string)GetValue(Text1Property);
            }
            set
            {
                SetValue(Text1Property, value);
            }
        }

        static void HandleText1Changed(
            DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UpdateText(d);
        }

        #endregion

        #region Text 2

        public static readonly DependencyProperty Text2Property
            = DependencyProperty.RegisterAttached(
                "Text2",
                typeof(string),
                typeof(AppBarToggleButton),
                new PropertyMetadata(HandleText2Changed));

        public string Text2
        {
            get
            {
                return (string)GetValue(Text2Property);
            }
            set
            {
                SetValue(Text2Property, value);
            }
        }

        static void HandleText2Changed(
            DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UpdateText(d);
        }

        #endregion

        #region Toggled

        public static DependencyProperty ToggledProperty
            = DependencyProperty.Register(
                "Toggled",
                typeof(bool),
                typeof(AppBarToggleButton),
                new PropertyMetadata(false, HandleToggledChanged));

        static void HandleToggledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UpdateToggleState(d);
        }

        public bool Toggled
        {
            get
            {
                return (bool)GetValue(ToggledProperty);
            }
            set
            {
                SetValue(ToggledProperty, value);
            }
        }

        #endregion

        #region Click Event

        public event EventHandler Click;

        void ApplicationBarIconButtonClick(object sender, EventArgs e)
        {
            ICommand command;
            object commandParameter;
            if (!Toggled)
            {
                command = Command1;
                commandParameter = CommandParameter1;
            }
            else
            {
                command = Command2 ?? Command1;
                commandParameter = CommandParameter2 ?? CommandParameter1;
            }

            if (command != null)
            {
                command.Execute(commandParameter);
            }
            else
            {
				/* No command to execute therefore rely entirely on the toggle state. */
				Toggled = !Toggled;
            }

            OnClick(e);
        }

        protected virtual void OnClick(EventArgs e)
        {
            var tempEvent = Click;
            if (tempEvent != null)
            {
                tempEvent(this, e);
            }
        }

        #endregion
    }
}